{
 this file is part of Ares
 Aresgalaxy ( http://aresgalaxy.sourceforge.net )

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*****************************************************************
 The following delphi code is based on Emule (0.46.2.26) Kad's implementation http://emule.sourceforge.net
 and KadC library http://kadc.sourceforge.net/
*****************************************************************
 }

{
Description:
DHT special 128 bit integer functions
}


unit int128;

interface

uses
  sysutils, windows, synsock;

type
  CU_INT128 = array[0..3] of cardinal;
  pCU_INT128 = ^CU_INT128;
  pbytearray = ^tbytearray;
  tbytearray = array[0..1023] of byte;

procedure CU_INT128_xor(inValue: pCu_INT128; value: pCu_INT128);
function CU_INT128_tohexstr(value: pCu_INT128; reversed: boolean = true): string;
procedure CU_INT128_fill(inValue: pCu_INT128; value: pCu_INT128); overload;
procedure CU_Int128_fill(m_data: pCU_INT128; value: pCU_INT128; numBits: cardinal); overload;

function CU_INT128_Compare(Value1: pCu_INT128; value2: pCu_INT128): boolean;
function CU_INT128_compareTo(m_data: pCU_INT128; value: cardinal): integer; overload;
function CU_Int128_compareTo(m_data: pCU_Int128; other: pCU_INT128): integer; overload;

function CU_INT128_getBitNumber(m_data: pCU_INT128; bit: cardinal): cardinal;
procedure CU_Int128_setBitNumber(m_data: pCU_INT128; bit: cardinal; value: cardinal);
procedure CU_Int128_shiftLeft(m_data: pCU_INT128; bits: cardinal);
procedure CU_Int128_setValue(m_data: pCU_INT128; value: cardinal);
procedure CU_Int128_add(m_data: pCU_INT128; value: pCU_Int128); overload;
procedure CU_Int128_add(m_data: pCU_INT128; value: cardinal); overload;
function CU_INT128_MinorOf(m_data: pCU_INT128; value: cardinal): boolean; overload;
function CU_INT128_MinorOf(m_data: pCU_INT128; value: pCU_INT128): boolean; overload;
function CU_INT128_Majorof(m_data: pCU_INT128; value: pCU_INT128): boolean;
procedure CU_Int128_toBinaryString(m_data: pCU_INT128; var str: string; trim: boolean = false);
procedure CU_Int128_setValueBE(m_data: pCU_INT128; valueBE: pbytearray);
procedure CU_INT128_fillNXor(Destination: pCU_INT128; initialValue: pCU_INT128; xorvalue: pCU_INT128);
procedure CU_INT128_copytoBuffer(source: pCU_INT128; destination: pbytearray);
procedure CU_INT128_copyFromBuffer(source: pbytearray; destination: pCU_INT128);

var
  m_data: CU_INT128;


implementation

uses
  dhtUtils, helper_strings;



procedure CU_INT128_copyFromBuffer(source: pbytearray; destination: pCU_INT128);
begin
  move(source[0], destination[0], 4);
  move(source[4], destination[1], 4);
  move(source[8], destination[2], 4);
  move(source[12], destination[3], 4);
end;

procedure CU_INT128_copytoBuffer(source: pCU_INT128; destination: pbytearray);
begin
  move(source[0], destination[0], 4);
  move(source[1], destination[4], 4);
  move(source[2], destination[8], 4);
  move(source[3], destination[12], 4);
end;

procedure CU_INT128_fillNXor(Destination: pCU_INT128; initialValue: pCU_INT128; xorvalue: pCU_INT128);
begin
  destination[0] := initialValue[0] xor xorvalue[0];
  destination[1] := initialValue[1] xor xorvalue[1];
  destination[2] := initialValue[2] xor xorvalue[2];
  destination[3] := initialValue[3] xor xorvalue[3];
end;

procedure CU_Int128_setValue(m_data: pCU_INT128; value: cardinal);
begin
  m_data[0] := 0;
  m_data[1] := 0;
  m_data[2] := 0;
  m_data[3] := value;
end;

procedure CU_Int128_setValueBE(m_data: pCU_INT128; valueBE: pbytearray);
var
  i: integer;
begin
  m_data[0] := 0;
  m_data[1] := 0;
  m_data[2] := 0;
  m_data[3] := 0;


  for i := 0 to 15 do
    m_data[i div 4] := m_data[i div 4] or (cardinal(valueBE[i]) shl (8 * (3 - (i mod 4))));

end;

procedure CU_Int128_shiftLeft(m_data: pCU_INT128; bits: cardinal);
var
  temp: CU_INT128;
  indexShift, i: integer;
  bit64Value, shifted: int64;
begin
  if ((bits = 0) or
    (((m_data[0] = 0) and
    (m_data[1] = 0) and
    (m_data[2] = 0) and
    (m_data[3] = 0))
    )
    ) then exit;

  if bits > 127 then begin
    CU_Int128_setValue(m_data, 0);
    exit;
  end;

  temp[0] := 0;
  temp[1] := 0;
  temp[2] := 0;
  temp[3] := 0;

  indexShift := integer(bits) div 32;
  shifted := 0;

  i := 3;
  while (i >= indexShift) do begin
    bit64Value := int64(m_data[i]);
    shifted := shifted + (bit64Value shl int64(bits mod 32));
    temp[i - indexShift] := cardinal(shifted);
    shifted := shifted shr 32;
    dec(i);
  end;

  for i := 0 to 3 do m_data[i] := temp[i];

end;

procedure CU_Int128_add(m_data: pCU_INT128; value: pCU_Int128);
var
  sum: int64;
  i: integer;
begin
  if CU_INT128_compareTo(value, 0) = 0 then exit;

  sum := 0;
  for i := 3 downto 0 do begin
    sum := sum + m_data[i];
    sum := sum + value[i];
    m_data[i] := cardinal(sum);
    sum := sum shr 32;
  end;

end;

procedure CU_Int128_add(m_data: pCU_INT128; value: cardinal);
var
  temp: CU_INT128;
begin
  if value = 0 then exit;

  CU_Int128_SetValue(@temp, value);
  CU_Int128_add(m_data, @temp);
end;


function CU_INT128_getBitNumber(m_data: pCU_INT128; bit: cardinal): cardinal;
var
  uLongNum, shift: integer;
begin
  result := 0;
  if (bit > 127) then exit;

  ulongNum := bit div 32;
  shift := 31 - (bit mod 32);
  result := ((m_data[ulongNum] shr shift) and 1);
end;

procedure CU_Int128_setBitNumber(m_data: pCU_INT128; bit: cardinal; value: cardinal);
var
  ulongNum, shift: integer;
begin
  ulongNum := bit div 32;
  shift := 31 - (bit mod 32);
  m_data[ulongNum] := m_data[ulongNum] or (1 shl shift);
  if value = 0 then m_data[ulongNum] := m_data[ulongNum] xor (1 shl shift);
end;

function CU_INT128_compareTo(m_data: pCU_INT128; value: cardinal): integer;
begin
  if ((m_data[0] > 0) or
    (m_data[1] > 0) or
    (m_data[2] > 0) or
    (m_data[3] > value)) then begin
    result := 1;
    exit;
  end;

  if m_data[3] < value then begin
    result := -1;
    exit;
  end;

  result := 0;
end;

function CU_INT128_Compare(Value1: pCu_INT128; value2: pCu_INT128): boolean;
begin
  result := ((Value1[0] = Value2[0]) and
    (Value1[1] = Value2[1]) and
    (Value1[2] = Value2[2]) and
    (Value1[3] = Value2[3]));
end;

procedure CU_INT128_xor(inValue: pCu_INT128; value: pCu_INT128);
begin
  inValue[0] := inValue[0] xor value[1];
  inValue[1] := inValue[1] xor value[1];
  inValue[2] := inValue[2] xor value[2];
  inValue[3] := inValue[3] xor value[3];
end;

procedure CU_INT128_fill(inValue: pCu_INT128; value: pCu_INT128);
begin
  inValue[0] := value[0];
  inValue[1] := value[1];
  inValue[2] := value[2];
  inValue[3] := value[3];
end;

procedure CU_Int128_fill(m_data: pCU_INT128; value: pCU_INT128; numBits: cardinal);
var
  i: integer;
  numULONGs: cardinal;
begin
 // Copy the whole ULONGs
  numULONGs := numBits div 32;
  for i := 0 to numULONGs - 1 do begin
    m_data[i] := value[i];
  end;

 // Copy the remaining bits
  for i := (32 * numULONGs) to numBits - 1 do CU_INT128_setBitNumber(m_data, i, CU_INT128_getBitNumber(value, i));
 // Pad with random bytes (Not seeding based on time to allow multiple different ones to be created in quick succession)
  for i := numBits to 127 do CU_INT128_setBitNumber(m_data, i, (random(2)));
end;

procedure CU_Int128_toBinaryString(m_data: pCU_INT128; var str: string; trim: boolean = false);
var
  b, i: integer;
begin
  str := '';

  for i := 0 to 127 do begin
    b := CU_Int128_getBitNumber(m_data, i);
    if ((not trim) or (b <> 0)) then begin
      str := str + Format('%d', [b]);
      trim := false;
    end;
  end;
  if length(str) = 0 then str := '0';

end;


function CU_INT128_tohexstr(value: pCu_INT128; reversed: boolean = true): string;
var
  num: cardinal;
begin
  setLength(result, 16);

  if reversed then begin
    num := synsock.ntohl(value[0]);
    move(num, result[1], 4);
    num := synsock.ntohl(value[1]);
    move(num, result[5], 4);
    num := synsock.ntohl(value[2]);
    move(num, result[9], 4);
    num := synsock.ntohl(value[3]);
    move(num, result[13], 4);
  end else begin
    move(value[0], result[1], 4);
    move(value[1], result[5], 4);
    move(value[2], result[9], 4);
    move(value[3], result[13], 4);
  end;

  result := bytestr_to_hexstr(result);
end;

function CU_INT128_MinorOf(m_data: pCU_INT128; value: cardinal): boolean;
begin
  result := (CU_INT128_compareTo(m_data, value) < 0);
end;

function CU_INT128_MinorOf(m_data: pCU_INT128; value: pCU_INT128): boolean; overload;
begin
  result := (CU_INT128_compareTo(m_data, value) < 0);
end;

function CU_INT128_Majorof(m_data: pCU_INT128; value: pCU_INT128): boolean; overload;
begin
  result := (CU_INT128_compareTo(m_data, value) > 0);
end;

function CU_Int128_compareTo(m_data: pCU_Int128; other: pCU_INT128): integer;
var
  i: integer;
begin
  result := 0;

  for i := 0 to 3 do begin
    if m_data[i] < other[i] then begin
      result := -1;
      exit;
    end;
    if m_data[i] > other[i] then begin
      result := 1;
      exit;
    end;
  end;
end;

end.
d.
